import {
  Observable, of, from,
  tap,
} from 'rxjs'

// 路由处理函数
function routerHandler() {
  console.log('pay...')
  return 'pay'
}

export class Logger1Interceptor {
  intercept(_, next) {
    console.log('Before1')
    const now = Date.now() // 当前时间戳
    return next.handle().pipe(
      tap({
        next: val => console.log(`After1... ${Date.now() - now}ms`, val)
      }),
    )
  }
}

export class Logger2Interceptor {
  intercept(_, next) {
    console.log('Before2')
    const now = Date.now() // 当前时间戳
    return next.handle().pipe(
      tap({
        next: val => console.log(`After2... ${Date.now() - now}ms`, val)
      }),
    )
  }
}

const logger1Interceptor = new Logger1Interceptor()
const logger2Interceptor = new Logger2Interceptor()

/**
 * 接收一个拦截器组成的数组 依次执行数组里面的每一个拦截器
 */
function callInterceptors(interceptors) {
  const nextFn = (i = 0): Observable<any> => {
    if (i === interceptors.length) { // 所有的前置拦截器执行完毕后
      return of(routerHandler()) // 执行路由处理函数 然后将路由处理函数的返回值包装在可观察对象中返回
    }
    // 根据 i 索引执行当前拦截器逻辑 时并传递2个参数 null + { handle:() => nextFn(i + 1) } 对象
    // 其中对象里面有一个 handle 属性 该属性的值是一个箭头函数里面包含了下一个拦截器的执行逻辑
    return interceptors[i].intercept(null, { handle:() => nextFn(i + 1) })
  }

  return nextFn()
}

const interceptors = [logger2Interceptor, logger1Interceptor]

callInterceptors(interceptors)
  .subscribe({
    next: val => console.log('next ', val),
    complete: () => console.log('complete'),
    error: err => console.log('error ', err),
  });

/**
 * 1. 实例化 Logger1Interceptor 类 获取 logger1Interceptor 拦截器实例
 * 2. 实例化 Logger2Interceptor 类 获取 logger2Interceptor 拦截器实例
 * 3. 将所有的拦截器实例都保存到 interceptors 变量中 [logger2Interceptor, logger1Interceptor] 
 *   3.1 后面需要依次从左往右拿出每一个拦截器进行处理
 * 4. 将 interceptors 传递到 callInterceptors 函数中作为参数 并对返回值调用 subscribe 函数
 *   4.1 内部定义一个 nextFn 函数 并且接收一个 i 作为参数 ( 默认值是 0  ) 表示当前需要执行拦截器数组中那一个拦截器
 *     4.1.1 这个 nextFn 执行逻辑称为洋葱模型 ( 此处是一个递归调用逻辑 为了注释方便采用扁平的方法书写 )
 *   4.2 执行 nextFn(0) 函数
 *     4.2.1 执行 logger2Interceptor.intercept(null, { handle:() => nextFn(1) }) 函数
 *     4.2.2 执行 logger2Interceptor 前置拦截器逻辑 执行完毕 再执行 () => nextFn(1) 此时跳转到步骤 4.3
 *     4.2.3 调用可观察对象的 pipe 方法时传递一个 tap 操作符 处理一些副作用逻辑后返回 可观察对象 此时所有的后置拦截器逻辑执行完毕
 *     4.2.4 返回可观察对象 结束
 *   4.3 执行 nextFn(1) 函数
 *     4.3.1 执行 logger1Interceptor.intercept(null, { handle:() => nextFn(2) }) 函数
 *     4.3.2 执行 logger1Interceptor 前置拦截器逻辑 执行完毕 再执行 () => nextFn(2) 此时跳转到步骤 4.4
 *     4.3.3 调用可观察对象的 pipe 方法时传递一个 tap 操作符 处理一些副作用逻辑后返回 可观察对象 ( 回到 4.2.2 步骤  执行 4.2.3 步骤 )
 *   4.4 执行 nextFn(2) 函数
 *     4.4.1 此时所有的前置拦截器逻辑都执行完毕 
 *     4.4.2 执行路由处理函数 并将路由处理函数的返回值包装在可观察对象中返回 此时开始执行后置拦截器逻辑 ( 回到 4.3.2 步骤  执行 4.3.3 步骤 )
 *  
 */
